%code requires {
	#include <memory>
	#include <string>
	#include <vector>
	#include "ast.hpp"

	using std::cout;
	using std::string;
	using std::unique_ptr;
	using std::vector;
}

%{

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "ast.hpp"
#include "exp.hpp"
#include "decl.hpp"

using namespace std;

int yylex();
void yyerror(unique_ptr<BaseAST> &ast, const char *s);
%}

%parse-param {unique_ptr<BaseAST> &ast }

%union {
	string *str_val;
	int int_val;
	BaseAST *ast_val;
	char char_val;
	vector<unique_ptr<BaseAST>> *ast_vec;
}

%token INT RETURN IF ELSE CONST LE GE EQ NE LAND LOR
%token <str_val> IDENT
%token <int_val> INT_CONST

%type <ast_val> FuncDef Block BlockItem FuncType Stmt			// 代码主体
%type <ast_val> Exp PrimaryExp UnaryExp				 			// 一元表达式
%type <ast_val> MulExp AddExp RelExp EqExp LAndExp LOrExp		// 二元表达式
%type <ast_val> Decl BType										// 定义
%type <ast_val> ConstDef ConstDecl ConstInitVal ConstExp		// 常量定义
%type <ast_val> VarDecl VarDef InitVal							// 变量定义 
%type <ast_vec> ConstDefs BlockItems VarDefs					// 贪婪匹配
%type <str_val> Number LVal										// 字面量
%type <char_val> UnaryOp										// 单目运算符

%%
CompUnit
	: FuncDef {
		auto comp_unit = make_unique<CompUnitAST>();
		comp_unit->func_def = unique_ptr<BaseAST>($1);
		ast = move(comp_unit);
	}
;

FuncDef
	: FuncType IDENT '(' ')' Block {
		auto ast = new FuncDefAST();
		ast->func_type = unique_ptr<BaseAST>($1);
		ast->ident = unique_ptr<string>($2);
		ast->block = unique_ptr<BaseAST>($5);
		$$ = ast;
	}
;

FuncType 
	: INT {
		auto ast = new FuncTypeAST();
		ast->type_name = unique_ptr<string>(new string("i32"));
		$$ = ast;
	}
;

Decl
	: ConstDecl {
		auto ast = new DeclAST();
		ast->type = DeclAST::TypeConstDecl;
		ast->sub_decl = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| VarDecl {
		auto ast = new DeclAST();
		ast->type = DeclAST::TypeVarDecl;
		ast->sub_decl = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
;

ConstDecl
	: CONST BType ConstDefs ';' {
		auto ast = new ConstDeclAST();
		ast->btype = unique_ptr<BaseAST>($2);
		ast->const_defs = unique_ptr<vector<unique_ptr<BaseAST>>>($3);
		$$ = ast;
	}
;

BType
	: INT {
		auto ast = new BTypeAST();
		ast->type_name = unique_ptr<string>(new string("i32"));
		$$ = ast;
	}
;

ConstDefs
	: ConstDef {
		auto ast_vec = new vector<unique_ptr<BaseAST>>;
		ast_vec->push_back(unique_ptr<BaseAST>($1));
		$$ = ast_vec;
	}
	| ConstDefs ',' ConstDef {
		auto ast_vec = $1;
		ast_vec->push_back(unique_ptr<BaseAST>($3));
		$$ = ast_vec;
	}
;

ConstDef
	: IDENT '=' ConstInitVal {
		auto ast = new ConstDefAST();
		ast->ident = unique_ptr<string>($1);
		ast->const_init_val = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

ConstInitVal
	: ConstExp {
		auto ast = new ConstInitValAST();
		ast->const_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
;

VarDecl
	: BType VarDefs ';' {
		auto ast = new VarDeclAST();
		ast->btype = unique_ptr<BaseAST>($1);
		ast->var_defs = unique_ptr<vector<unique_ptr<BaseAST>>>($2);
		$$ = ast;
	}
;

VarDefs
	: VarDef {
		auto var_defs = new vector<unique_ptr<BaseAST>>;
		var_defs->push_back(unique_ptr<BaseAST>($1));
		$$ = var_defs;
	}
	| VarDefs ',' VarDef {
		auto var_defs = $1;
		var_defs->push_back(unique_ptr<BaseAST>($3));
		$$ = var_defs;
	}
;

VarDef
	: IDENT {
		auto ast = new VarDefAST();
		ast->type = VarDefAST::TypeDecl;
		ast->ident = unique_ptr<string>($1);
		$$ = ast;
	}
	| IDENT '=' InitVal {
		auto ast = new VarDefAST();
		ast->type = VarDefAST::TypeDef;
		ast->ident = unique_ptr<string>($1);
		ast->init_val = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

InitVal
	: Exp {
		auto ast = new InitValAST();
		ast->exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
;
Block
	: '{' BlockItems '}' {
		auto ast = new BlockAST();
		ast->block_items = unique_ptr<vector<unique_ptr<BaseAST>>>($2);
		$$ = ast;
	}
;

BlockItems
	: { $$ = new vector<unique_ptr<BaseAST>>(); }
	| BlockItems BlockItem {
		auto ast_vec = $1;
		(*ast_vec).push_back(unique_ptr<BaseAST>($2));
		$$ = ast_vec;
	}
;

BlockItem
	: Decl {
		auto ast = new BlockItemAST();
		ast->type = BlockItemAST::TypeDecl;
		ast->sub_ast = unique_ptr<BaseAST> ($1);
		$$ = ast;
	}
	| Stmt {
		auto ast = new BlockItemAST();
		ast->type = BlockItemAST::TypeStmt;
		ast->sub_ast = unique_ptr<BaseAST> ($1);
		$$ = ast;
	}
;

Stmt
	: LVal '=' Exp ';' {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeLVal;
		ast->lval = unique_ptr<string>($1);
		ast->first_ast = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| ';' {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeNone;
		$$ = ast;
	}
	| Exp ';' {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeSingle;
		ast->first_ast = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| Block {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeBlock;
		ast->first_ast = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| IF '(' Exp ')' Stmt {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeIf;
		ast->first_ast = unique_ptr<BaseAST>($3);
		ast->second_ast = unique_ptr<BaseAST>($5);
		$$ = ast;
	}
	| IF '(' Exp ')' Stmt ELSE Stmt {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeIfElse;
		ast->first_ast = unique_ptr<BaseAST>($3);
		ast->second_ast = unique_ptr<BaseAST>($5);
		ast->third_ast = unique_ptr<BaseAST>($7);
		$$ = ast;
	}
	| RETURN Exp ';' {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeReturn;
		ast->first_ast = unique_ptr<BaseAST>($2);
		$$ = ast;
	}
	| RETURN ';' {
		auto ast = new StmtAST();
		ast->type = StmtAST::TypeReturnNone;
		$$ = ast;
	}
;

Exp
	: LOrExp {
		auto ast = new ExpAST();
		ast->unary_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
;

LVal
	: IDENT {
		$$  = $1;
	}
;

PrimaryExp
	: '(' Exp ')' {
		auto ast = new PrimaryExpAST();
		ast->type = PrimaryExpAST::TypeExp;
		ast->exp = unique_ptr<BaseAST>($2);
		$$ = ast;
	}
	| LVal {
		auto ast = new PrimaryExpAST();
		ast->type = PrimaryExpAST::TypeLVal;
		ast->ident = unique_ptr<string>($1);
		$$ = ast;
	}
	| Number {
		auto ast = new PrimaryExpAST();
		ast->type = PrimaryExpAST::TypeNumber;
		ast->ident = unique_ptr<string>($1);
		$$ = ast;
	}
;

Number
	: INT_CONST {
		$$ = new string(to_string($1));
	}
;

UnaryExp
	: PrimaryExp {
		auto ast = new UnaryExpAST();
		ast->type = UnaryExpAST::TypePrimaryExp;
		ast->exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| UnaryOp UnaryExp {
		auto ast = new UnaryExpAST();
		ast->type = UnaryExpAST::TypeUnaryExp;
		ast->unary_op = $1;
		ast->exp = unique_ptr<BaseAST>($2);
		$$ = ast;
	}

UnaryOp
	: '+' { $$ = '+'; }
	| '-' { $$ = '-'; }
	| '!' { $$ = '!'; }
;

MulExp
	: UnaryExp  {
		auto ast = new MulExpAST();
		ast->type = MulExpAST::TypeUnaryExp;
		ast->unary_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| MulExp '*' UnaryExp {
		auto ast = new MulExpAST();
		ast->type = MulExpAST::TypeMul;
		ast->mul_exp = unique_ptr<BaseAST>($1);
		ast->unary_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| MulExp '/' UnaryExp {
		auto ast = new MulExpAST();
		ast->type = MulExpAST::TypeDiv;
		ast->mul_exp = unique_ptr<BaseAST>($1);
		ast->unary_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| MulExp '%' UnaryExp {
		auto ast = new MulExpAST();
		ast->type = MulExpAST::TypeMod;
		ast->mul_exp = unique_ptr<BaseAST>($1);
		ast->unary_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

AddExp
	: MulExp {
		auto ast = new AddExpAST();
		ast->type = AddExpAST::TypeMulExp;
		ast->mul_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| AddExp '+' MulExp {
		auto ast = new AddExpAST();
		ast->type = AddExpAST::TypeAdd;
		ast->add_exp = unique_ptr<BaseAST>($1);
		ast->mul_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| AddExp '-' MulExp {
		auto ast = new AddExpAST();
		ast->type = AddExpAST::TypeSub;
		ast->add_exp = unique_ptr<BaseAST>($1);
		ast->mul_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

RelExp
	: AddExp {
		auto ast = new RelExpAST();
		ast->type = RelExpAST::TypeAddExp;
		ast->add_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| RelExp '<' AddExp {
		auto ast = new RelExpAST();
		ast->type = RelExpAST::TypeLT;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		ast->add_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| RelExp '>' AddExp {
		auto ast = new RelExpAST();
		ast->type = RelExpAST::TypeGT;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		ast->add_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| RelExp LE AddExp {
		auto ast = new RelExpAST();
		ast->type = RelExpAST::TypeLE;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		ast->add_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| RelExp GE AddExp {
		auto ast = new RelExpAST();
		ast->type = RelExpAST::TypeGE;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		ast->add_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

EqExp
	: RelExp {
		auto ast = new EqExpAST();
		ast->type = EqExpAST::TypeRelExp;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| EqExp EQ RelExp {
		auto ast = new EqExpAST();
		ast->type = EqExpAST::TypeEQ;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		ast->eq_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
	| EqExp NE RelExp {
		auto ast = new EqExpAST();
		ast->type = EqExpAST::TypeNE;
		ast->rel_exp = unique_ptr<BaseAST>($1);
		ast->eq_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

LAndExp
	: EqExp {
		auto ast = new LAndExpAST();
		ast->type = LAndExpAST::TypeEqExp;
		ast->eq_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| LAndExp LAND EqExp {
		auto ast = new LAndExpAST();
		ast->type = LAndExpAST::TypeLAnd;
		ast->land_exp = unique_ptr<BaseAST>($1);
		ast->eq_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

LOrExp
	: LAndExp {
		auto ast = new LOrExpAST();
		ast->type = LOrExpAST::TypeLAndExp;
		ast->land_exp = unique_ptr<BaseAST>($1);
		$$ = ast;
	}
	| LOrExp LOR LAndExp {
		auto ast = new LOrExpAST();
		ast->type = LOrExpAST::TypeLOr;
		ast->lor_exp = unique_ptr<BaseAST>($1);
		ast->land_exp = unique_ptr<BaseAST>($3);
		$$ = ast;
	}
;

ConstExp
	: Exp {
		auto ast = new ConstExpAST();
		ast->exp = unique_ptr<BaseAST>($1);
	}
;

%%

void yyerror(unique_ptr<BaseAST> &ast, const char *s) {
	cerr << "error: " << s << endl;
}

